package com.ibm.spatiotemp.tools;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

import com.ibm.spatiotemp.server.datasources.DataSchema;
import com.ibm.spatiotemp.server.datasources.DataSource;

public class IfxConnectionPool {
	
	private String driver = "com.informix.jdbc.IfxDriver";
	private String url = null;
	private DataSchema dataschema = null;

	private int maxConnections;

	private boolean waitIfBusy;

	private Vector<Connection> availableConnections; 
	
	private Vector<Connection> busyConnections;  // for close
	
	public IfxConnectionPool(DataSource ds) throws SQLException {
		this.url = ds.getConnection_string();
		this.dataschema = ds.getDataschema();
	    this.maxConnections = Constants.DEFAULTMAXCONNECTIONS;
	    this.waitIfBusy = true;
	    availableConnections = new Vector<Connection>(maxConnections);
	    busyConnections = new Vector<Connection>();
	    for(int i=0; i<maxConnections; i++) {
	      availableConnections.addElement(newConnection());
	    }
	}

	public synchronized Connection getConnection() throws SQLException, InterruptedException {
		while (availableConnections.isEmpty()) {
			if (waitIfBusy) {
				wait();
			} else {
				throw new SQLException("Connection limit reached");
			}
		}
		Connection existingConnection = (Connection)availableConnections.lastElement();
		int lastIndex = availableConnections.size() - 1;
		availableConnections.removeElementAt(lastIndex);
		// If connection on available list is closed, create a connection to replace it			
		if (existingConnection.isClosed()) {
			existingConnection = newConnection();
		}
		busyConnections.addElement(existingConnection);
		return(existingConnection);
	} 
	
	private Connection newConnection() throws SQLException {
		try {
			Class.forName(driver);
		} catch (ClassNotFoundException e) {
			throw new SQLException("Can't find class for driver: " + driver);
		}
		Connection newConn = null;
		newConn = DriverManager.getConnection(url);
		newConn.setAutoCommit(true);
		Statement st = newConn.createStatement();
		st.execute("set lock mode to wait 30;set isolation to dirty read;set pdqpriority 90;");
		st.close();
		return newConn;
	}

	public synchronized void free(Connection connection) {
	    busyConnections.removeElement(connection);
	    availableConnections.addElement(connection);
	    // Wake up threads that are waiting for a connection
	    notifyAll();
	}

	
	public synchronized void closeAllConnections() {
	    closeConnections(availableConnections);
	    closeConnections(busyConnections);
	  }

	private void closeConnections(Vector<Connection> connections) {
		try {
			for(int i=0; i<connections.size(); i++) {
				Connection connection = (Connection)connections.elementAt(i);
				if (!connection.isClosed()) {
					connection.close();
				}
			}
		} catch(SQLException sqle) {
		}
	} 

	public synchronized String toString() {
		String info ="ConnectionPool(" + url + ", available=" + availableConnections.size() +
				", busy=" + busyConnections.size() +
				", max=" + maxConnections;
		return(info);
	}

	public DataSchema getDataschema() {
		return dataschema;
	}
	
	public String checkPrimaryKeySql(String id) {
		String format = "select %s from %s where %s=\'%s\'";
		return String.format(format, dataschema.getMovobj_colname(), Constants.TABLE_NAME, dataschema.getMovobj_colname(), id);
	}
	
	public String initPrimaryKeySql(String id) {
		String format = "insert into %s(%s, %s) values (\'%s\', %s)";
		return String.format(format, Constants.TABLE_NAME, dataschema.getMovobj_colname(), Constants.TSCOLUMN_NAME,	id, dataschema.getOther());
	}
	
}


